import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;

public class MainCardPanelTest {

	private GameSystem system;
	
	@Before
	public void setUp() throws Exception {
		// system for testing
		system = new GameSystem();
		
		// initialize players
		system.getParty().getLeader().setName("Sam");
		system.getParty().getLeader().setOccupation(Occupation.SOLDIER);
		
		system.getParty().addTraveller(new Member("Andrew"));
		system.getParty().addTraveller(new Member("Hyangjin"));
		system.getParty().addTraveller(new Member("Jeff"));
		system.getParty().addTraveller(new Member("Robert"));
	}

	/**
	 * Regular buying food, water, and ammunition -Sam
	 */
	@Test
	public void shopTest1() {
		
		// Initialization Tests
		assertEquals("Initializition of money not 300", 300, system.getParty().getMoney());
		assertEquals("Initialization of weight not 0", 0, system.getParty().getWeight());
		
		assertEquals("Initialization of food not 50", 50, system.getParty().getFood());
		assertEquals("Initialization of water not 100", 100, system.getParty().getWater());
		assertEquals("Initialization of ammunition not 0", 0, system.getParty().getAmmunition());
		
		
		// Not enough money
		system.getParty().setMoney(10);		// initially decrease money to 10
		
		int foodChange = 20;
		int waterChange = 20;
		int ammunitionChange = 10;
		
		int moneySpent = foodChange + waterChange + ammunitionChange;
		int weightSpent = foodChange + waterChange + ammunitionChange;
		
		assertFalse("States true when there is not enough money", system.getParty().updateMoney(-moneySpent));
		assertTrue("States false when there is enough space", system.getParty().updateWeight(weightSpent));
		
		
		// Not enough capacity
		system.getParty().setMoney(300);
		system.getParty().setWeight(180);		// initially decrease weight to 180
		
		foodChange = 20;
		waterChange = 20;
		ammunitionChange = 10;
		
		moneySpent = foodChange + waterChange + ammunitionChange;
		weightSpent = foodChange + waterChange + ammunitionChange;
		
		assertTrue("States false when there is enough money", system.getParty().updateMoney(-moneySpent));
		assertFalse("States true when there is not enough space", system.getParty().updateWeight(weightSpent));
		
		
		// Normal buying food, water, and ammunition
		system.getParty().setMoney(300);
		system.getParty().setWeight(0);
		
		foodChange = 20;
		waterChange = 20;
		ammunitionChange = 10;
		
		moneySpent = foodChange + waterChange + ammunitionChange;
		weightSpent = foodChange + waterChange + ammunitionChange;
		
		assertTrue("States false when there is enough money", system.getParty().updateMoney(-moneySpent));
		assertTrue("States false when there is enough space", system.getParty().updateWeight(weightSpent));
		
		assertEquals("Money is not decreased by the amount spent", 250, system.getParty().getMoney());
		assertEquals("Weight is not increased by the weight gained", 50, system.getParty().getWeight());
		
		system.getShop().buyFood(foodChange);	
		system.getShop().buyWater(waterChange);
		system.getShop().buyAmmunition(ammunitionChange);
		
		assertEquals("Food is not increased by the amount of food bought", 70, system.getParty().getFood());
		assertEquals("Water is not increased by the amount of water bought", 120, system.getParty().getWater());
		assertEquals("Ammunition is not increased by the amount of ammunition bought", 10, system.getParty().getAmmunition());
		
	}
	
	/**
	 * Regular buying weapon and other items -Sam
	 */
	@Test
	public void shopTest2() {
		assertEquals("Initializition of money not 300", 300, system.getParty().getMoney());
		assertEquals("Initialization of weight not 0", 0, system.getParty().getWeight());
		
		assertTrue("Initialization of inventory is not empty", system.getParty().getInventory().isEmpty());
		assertEquals("Initialization of weapon is not none", "none", system.getParty().getWeapon().toString());
		
		// Not enough money
		int itemChange = 30;
		int weaponChange = 50;
		
		int moneySpent = itemChange + weaponChange;
		int weightSpent = itemChange + weaponChange;
		
		assertTrue("States false when there is enough money", system.getParty().updateMoney(-moneySpent));
		assertTrue("States false when there is enough space", system.getParty().updateWeight(weightSpent));
		
		assertEquals("Money is not decreased by the amount spent", 220, system.getParty().getMoney());
		assertEquals("Weight is not increased by the weight gained", 80, system.getParty().getWeight());
		
		Item itemToAdd = new Item(Item.ItemType.binoculars);
		system.getParty().addToInventory(itemToAdd);
		system.getParty().setWeapon(Party.Weapon.ak47);
		
		assertEquals("Inventory size not increased when item added", 1, system.getParty().getInventory().size());
		assertTrue("Correct weapon is not added to the inventory", system.getParty().getInventory().contains(itemToAdd));
		assertEquals("Weapon is not set according to the weapon bought", "ak47", system.getParty().getWeapon().toString());
	}
	
	/**
	 * Checks the BarbedWire.dealWithBarbedWire method.
	 * Makes sure that the members are hurt at the right time
	 * when jumping over barbed wire and makes sure that wire cutters
	 * are damaged at the right time when cutting the barbed wire.
	 * Also depends on proper functioning of Member and BarbedWire
	 * constructors.
	 */
	@Test
	public void dealWithBarbedWireTest() {
		
		//Initialize 4 parties to test jumping over the wire and 4 barbed wire fences.
			Party high_jumpers = initialize_party();
			Party low_jumpers = initialize_party();
			Party low_jumper_third = initialize_party();
			Party high_jumper_third = initialize_party();
		
			
			BarbedWire fence_to_be_jumped_by_high_jumpers = new BarbedWire(null, high_jumpers);
			for (Member m:high_jumpers.getTravellers())
				m.setTestingJump(true, fence_to_be_jumped_by_high_jumpers.getHeight()+1);
					
			BarbedWire fence_to_be_jumped_by_low_jumpers = new BarbedWire(null, low_jumpers);
			for (Member m:low_jumpers.getTravellers())
				m.setTestingJump(true, fence_to_be_jumped_by_low_jumpers.getHeight()-1);
					
			BarbedWire fence_to_be_jumped_by_low_jumper_third = new BarbedWire(null, low_jumper_third);
			for (Member m:low_jumper_third.getTravellers())
				m.setTestingJump(true, fence_to_be_jumped_by_low_jumper_third.getHeight()+1);
			Member thirdMember = low_jumper_third.getTravellers().get(2);
			thirdMember.setTestingJump(true, fence_to_be_jumped_by_low_jumper_third.getHeight()-1);
					
					
			BarbedWire fence_to_be_jumped_by_high_jumper_third = new BarbedWire(null, high_jumper_third);
			for (Member m:high_jumper_third.getTravellers())
				m.setTestingJump(true, fence_to_be_jumped_by_high_jumper_third.getHeight()-1);
			thirdMember = high_jumper_third.getTravellers().get(2);
			thirdMember.setTestingJump(true, fence_to_be_jumped_by_high_jumper_third.getHeight()+1);
		//Test jumping
			RandomEvent mishap;
			mishap = fence_to_be_jumped_by_high_jumpers.dealWithBarbedWire(false);
			assertEquals("everyone should have made it over the fence, but someone got hurt.",
						 mishap,
						 null);
			
			mishap = fence_to_be_jumped_by_low_jumpers.dealWithBarbedWire(false);
			assertTrue("Everyone's jump value was too short, but no one got hurt.", mishap!=null);
			boolean works = false;
			if (mishap instanceof Injury)
			{
				Injury i = (Injury) mishap;
				works = i.getVictim() == low_jumpers.getTravellers().get(0);
			}
			assertTrue("Everyone's jump value was too short, the first guy should have been injured but someone else was", works);
			
			mishap = fence_to_be_jumped_by_low_jumper_third.dealWithBarbedWire(false);
			assertTrue("Third member jumped too short, but no one got hurt.", mishap!=null);
			works = false;
			if (mishap instanceof Injury)
			{
				Injury i = (Injury) mishap;
				works = i.getVictim() == low_jumper_third.getTravellers().get(2);
			}
			assertTrue("The third guy should have been injured but someone else was", works);
			
			mishap = fence_to_be_jumped_by_high_jumper_third.dealWithBarbedWire(false);
			assertTrue("Three members jump values too short, but no one got hurt.", mishap!=null);
			works = false;
			if (mishap instanceof Injury)
			{
				Injury i = (Injury) mishap;
				works = i.getVictim() == high_jumper_third.getTravellers().get(0);
			}
			assertTrue("The first guy should have been injured but someone else was", works);
			
		//Initialize two parties to test wire cutting. One with healthy cutters and one with damaged cutters
			double wireThickness = 0.40;
			Party party_with_healthy_wire_cutters = initialize_party();
			Item healthyWireCutters = new Item(Item.ItemType.wire_cutters);
			party_with_healthy_wire_cutters.addToInventory(healthyWireCutters);
			
			Party party_with_damaged_wire_cutters = initialize_party();
			Item damagedWireCutters = new Item(Item.ItemType.wire_cutters);
			damagedWireCutters.damage(0.7); //damage the wire cutters down to 30%
			party_with_damaged_wire_cutters.addToInventory(damagedWireCutters);
			
			BarbedWire fence_that_wont_break_the_cutters = new BarbedWire(null, party_with_healthy_wire_cutters);
			BarbedWire fence_that_still_wont_break_the_cutters = new BarbedWire(null, party_with_healthy_wire_cutters);
			BarbedWire fence_that_will_finally_break_the_cutters = new BarbedWire(null, party_with_healthy_wire_cutters);
			BarbedWire fence_that_will_break_the_cutters = new BarbedWire(null, party_with_damaged_wire_cutters);
			fence_that_wont_break_the_cutters.setThickness(wireThickness);
			fence_that_still_wont_break_the_cutters.setThickness(wireThickness);
			fence_that_will_finally_break_the_cutters.setThickness(wireThickness);
			fence_that_will_break_the_cutters.setThickness(wireThickness);
			
		//Test wire cutting action!
			mishap = fence_that_wont_break_the_cutters.dealWithBarbedWire(true);
			assertTrue("The cutters shouldn't have broke but they did", mishap==null);
			assertTrue("The cutter's damaged was reduced by the wrong amount",  
						60 == healthyWireCutters.getDurability());
			
			mishap = fence_that_still_wont_break_the_cutters.dealWithBarbedWire(true);
			assertTrue("The cutters still shouldn't have broke but they did", mishap==null);
			assertTrue("The cutter's damaged was reduced by the wrong amount",  
					     20 == healthyWireCutters.getDurability());
			
			mishap = fence_that_will_finally_break_the_cutters.dealWithBarbedWire(true);
			assertTrue("Wire cutters should have broke but they didn't.", mishap!=null);
			
			mishap = fence_that_will_break_the_cutters.dealWithBarbedWire(true);
			assertTrue("Wire cutters should have broke but they didn't", mishap!=null);
			
		
			
		
		
		
	}
	
	
	/**
	 * 
	 * @return A party with Members named "a", "b", "c", and "d", in that order.
	 */
	private Party initialize_party() {
		//Initialize new GameSystem
			system = new GameSystem();

			//Initialize players
			system.getParty().getLeader().setName("leader");
			system.getParty().getLeader().setOccupation(Occupation.SOLDIER);
			system.getParty().addTraveller(new Member("a"));
			system.getParty().addTraveller(new Member("b"));
			system.getParty().addTraveller(new Member("c"));
			system.getParty().addTraveller(new Member("d"));
			
			//Change information about the GameSystem
			system.setDaysLeft(12);//Set days left to 12
			system.setLose(true);//Set lose to true since it's false by default
			
			//Change information about the Party
			system.getParty().setFood(11);
			system.getParty().setWater(22);
			system.getParty().setMoney(33);
			system.getParty().setAmmunition(44);
			system.getParty().setDistance(55);
			system.getParty().setWeight(66);
			system.getParty().setWeapon(Party.Weapon.ak47);
			system.getParty().setCurrent(new City("Fairy Land"));
			
			return system.getParty();
	}
	
	
	/**
	 * Check that all information about the GameSystem
	 * gets saved and loaded correctly - Andrew Kim 
	 */
	@Test
	public void saveLoadTest(){
		//Initialize new GameSystem
		system = new GameSystem();

		//Initialize players
		system.getParty().getLeader().setName("Sam");
		system.getParty().getLeader().setOccupation(Occupation.SOLDIER);
		system.getParty().addTraveller(new Member("Andrew"));
		system.getParty().addTraveller(new Member("Hyangjin"));
		system.getParty().addTraveller(new Member("Jeff"));
		system.getParty().addTraveller(new Member("Robert"));
		
		//Change information about the GameSystem
		system.setDaysLeft(12);//Set days left to 12
		system.setLose(true);//Set lose to true since it's false by default
		
		//Change information about the Party
		system.getParty().setFood(11);
		system.getParty().setWater(22);
		system.getParty().setMoney(33);
		system.getParty().setAmmunition(44);
		system.getParty().setDistance(55);
		system.getParty().setWeight(66);
		system.getParty().setWeapon(Party.Weapon.ak47);
		system.getParty().setCurrent(new City("Fairy Land"));
		
		//Let's hurt the players
		system.getParty().getTravellers().get(0).setHealth(99);
		system.getParty().getTravellers().get(1).setHealth(88);
		system.getParty().getTravellers().get(2).setHealth(77);
		system.getParty().killPlayer(3);
		
		//Save
		system.saveGame();//Save the game
		system = new GameSystem();//Create a new GameSystem
		
		//Make sure that the last system's data got cleared out
		assertFalse("Leader's name  not get flushed",system.getParty().getLeader().getName().equals("Sam"));
		assertFalse("First member's name did not get flushed",system.getParty().getLeader().equals("Andrew"));
		assertFalse("Second member's name did not get flushed",system.getParty().getLeader().equals("Hyangjin"));
		assertFalse("Third member's name did not get flushed",system.getParty().getLeader().equals("Jeff"));
		assertFalse("Fourth member's name did not get flushed",system.getParty().getLeader().equals("Robert"));
		assertTrue("Days left did not get flushed",system.getDaysLeft()==100);
		assertFalse("Lose boolean did not get flushed",system.getLose());
		//This should be enough to prove that the new instance does not have any data carried over.
		
		//Load the information
		system.loadGame();
		
		//Check system variables
		assertEquals("Days left did not get loaded properly",12,system.getDaysLeft());
		assertTrue("Lose boolean did not get saved properly",system.getLose());
		
		//Check if the names are saved correctly
		assertEquals("Leader's name is not loaded properly","Sam",system.getParty().getLeader().getName());//Check leader's name
		assertEquals("First member's name does not match","Andrew",system.getParty().getTravellers().get(0).getName());//Check the first member's name
		assertEquals("Second member's name does not match","Hyangjin",system.getParty().getTravellers().get(1).getName());//Check the first member's name
		assertEquals("Third member's name does not match","Jeff",system.getParty().getTravellers().get(2).getName());//Check the first member's name
		assertEquals("Fourth member's name does not match","Robert",system.getParty().getTravellers().get(3).getName());//Check the first member's name
		
		//Check information about the Party
		assertEquals("Food did not get saved properly",system.getParty().getFood(),11);
		assertEquals("Water did not get saved properly",22,system.getParty().getWater());
		assertEquals("Money did not get saved properly",33,system.getParty().getMoney());
		assertEquals("Ammunition did not get saved properly",44,system.getParty().getAmmunition());
		assertEquals("Distance did not get saved properly",55,system.getParty().getDistance());
		assertEquals("Weight did not get saved properly",66,system.getParty().getWeight());
		assertEquals("Weapon did not get saved proerply",Party.Weapon.ak47,system.getParty().getWeapon());
		assertEquals("Current location did not get saved properly","Fairy Land",system.getParty().getCurrent().toString());
		
		//Check player statuses
		assertEquals("First member's health did not get saved properly",99,system.getParty().getTravellers().get(0).getHealth());
		assertEquals("Second member's health did not get saved properly",88,system.getParty().getTravellers().get(1).getHealth());
		assertEquals("Third member's health did not get saved properly",77,system.getParty().getTravellers().get(2).getHealth());
		assertEquals("Fourth member's health did not get saved properly",0,system.getParty().getTravellers().get(3).getHealth());
		assertFalse("Fourth member's dead status did not get saved properly",system.getParty().getTravellers().get(3).getAlive());
	}
	
	/**
	 * Tests if the map location is stored correctly.
	 * @author Robert Lee
	 */
	public void testMapHandling(){
		Party testParty = initialize_party();
		
		testParty.setCurrent(new City("Bremen"));
		assertEquals("The city could not be set properly.","Bremen",testParty.getCurrent().getName());
		
		testParty.setCurrent(new Fields(new City("Bremen"),"Oldenburg",0,new ArrayList<Location>()));
		assertEquals("The field could not be set properly.","Oldenburg",testParty.getCurrent().getName());
		
	}
	

}
